package org.example.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Callable;
import java.util.function.Predicate;

public class RetryUtils {
    private static final Logger logger = LoggerFactory.getLogger(RetryUtils.class);

    /**
     * 带有重试功能的方法执行器
     * 
     * @param callable       要执行的操作
     * @param maxRetries     最大重试次数
     * @param retryCondition 重试条件
     * @param sleepMillis    重试间隔
     * @param <T>            返回类型
     * @return 执行结果
     * @throws Exception 如果所有重试都失败，则抛出最后一次的异常
     */
    public static <T> T executeWithRetry(
            Callable<T> callable,
            int maxRetries,
            Predicate<Exception> retryCondition,
            long sleepMillis) throws Exception {

        Exception lastException = null;

        for (int attempt = 0; attempt <= maxRetries; attempt++) {
            try {
                if (attempt > 0) {
                    logger.info("重试操作，尝试次数: {}/{}", attempt, maxRetries);
                }

                return callable.call();
            } catch (Exception e) {
                lastException = e;

                // 判断是否需要重试
                if (attempt >= maxRetries || !retryCondition.test(e)) {
                    logger.error("操作失败，不再重试", e);
                    throw e;
                }

                logger.warn("操作失败，准备重试: {}", e.getMessage());

                // 重试前等待
                if (sleepMillis > 0) {
                    try {
                        Thread.sleep(sleepMillis);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException("重试过程被中断", ie);
                    }
                }
            }
        }

        // 这里实际上不会执行到，因为最后一次失败会在循环中抛出异常
        throw new RuntimeException("所有重试都失败", lastException);
    }

    /**
     * 判断异常是否为超时类型
     */
    public static boolean isTimeoutException(Exception e) {
        String message = e.getMessage().toLowerCase();
        return e instanceof java.util.concurrent.TimeoutException
                || message.contains("timeout")
                || message.contains("timed out")
                || message.contains("idle");
    }
}